#include "common/pch.h"
#include "metadata/creaturedata.h"

#include <format>

#include <cftf/loger/syslog.h>
#include <cftc/reflect/class.h>
#include <cftc/reflect/field.h>

#include "dbc/structure.h"
#include "metadata/creatureinfo.h"
#include "metadata/equipment_info.h"
#include "metadata/equipment_info_raw.h"
#include "object/creature.h"
#include "object/objectmgr.h"

namespace mt
{

	CFT_IMPLEMENT_REFLECT_CLASS_ANNOTATION_BEGIN(CreatureData, cftc::reflect::Object, R"(wow_meta:"custom_loader_sql:mt.sql_template.CreatureData_Loader")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(guid, R"(wow_meta:"field_name:guid")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(id, R"(wow_meta:"field_name:id")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(mapid, R"(wow_meta:"field_name:map")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(modelid_override, R"(wow_meta:"field_name:modelid")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(equipmentId, R"(wow_meta:"field_name:equipment_id")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(posX, R"(wow_meta:"field_name:position_x")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(posY, R"(wow_meta:"field_name:position_y")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(posZ, R"(wow_meta:"field_name:position_z")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(orientation, R"(wow_meta:"field_name:orientation")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(spawntimesecs, R"(wow_meta:"field_name:spawntimesecs")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(spawndist, R"(wow_meta:"field_name:spawndist")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(currentwaypoint, R"(wow_meta:"field_name:currentwaypoint")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(curhealth, R"(wow_meta:"field_name:curhealth")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(curmana, R"(wow_meta:"field_name:curmana")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(is_dead, R"(wow_meta:"field_name:DeathState")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(movementType, R"(wow_meta:"field_name:MovementType")");

		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(gameEvent, R"(wow_meta:"field_name:event")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(GuidPoolId, R"(wow_meta:"field_name:GuidPoolId")");
		CFT_IMPLEMENT_REFLECT_FIELD_ANNOTATION(EntryPoolId, R"(wow_meta:"field_name:EntryPoolId")");
	CFT_IMPLEMENT_REFLECT_CLASS_END();


	void CreatureData::MetaInstanceInit1()
	{
		auto map_dbc = dbc::MapEntry::GetElement(mapid);
		if (!map_dbc) {
			cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {}) that spawned at nonexistent map (Id: {}), skipped.", guid, mapid));
			return;
		}
		auto creature_info_meta = CreatureInfo::GetElement(id);
		if (!creature_info_meta)
		{
			cftf::loger::SysLog::Instance().Error(std::format("Table `creature` has creature (GUID: {}) with non existing creature entry {}, skipped.", guid, id));
			return;
		}
		if (modelid_override > 0 && !dbc::CreatureDisplayInfoEntry::GetElement(modelid_override))
		{
			cftf::loger::SysLog::Instance().Error(std::format("Table `creature` GUID {} (entry {}) has model for nonexistent model id ({}), set to 0.", guid, id, modelid_override));
			modelid_override = 0;
		}

		if (equipmentId > 0)                           // -1 no equipment, 0 use default
		{
			if (!mt::EquipmentInfo::GetElement(equipmentId) && !mt::EquipmentInfoRaw::GetElement(equipmentId))
			{
				cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (Entry: {}) with equipment_id {} not found in table `creature_equip_template` or `creature_equip_template_raw`, set to no equipment.", id, equipmentId));
				equipmentId = -1;
			}
		}

		if (creature_info_meta->RegenerateStats & REGEN_FLAG_HEALTH && curhealth < creature_info_meta->MinLevelHealth)
		{
			cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {} Entry: {}) with `creature_template`.`RegenerateStats` & REGEN_FLAG_HEALTH and low current health ({}), `creature_template`.`MinLevelHealth`={}.", guid, id, curhealth, creature_info_meta->MinLevelHealth));
			curhealth = creature_info_meta->MinLevelHealth;
		}

		if (creature_info_meta->ExtraFlags & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
		{
			if (!map_dbc || !map_dbc->IsDungeon())
				cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {} Entry: {}) with `creature_template`.`ExtraFlags` including CREATURE_FLAG_EXTRA_INSTANCE_BIND ({}) but creature are not in instance.",
					guid, id, (int)CREATURE_FLAG_EXTRA_INSTANCE_BIND));
		}

		if (creature_info_meta->ExtraFlags & CREATURE_FLAG_EXTRA_AGGRO_ZONE)
		{
			if (!map_dbc || !map_dbc->IsDungeon())
				cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {} Entry: {}) with `creature_template`.`ExtraFlags` including CREATURE_FLAG_EXTRA_AGGRO_ZONE ({}) but creature are not in instance.",
					guid, id, (int)CREATURE_FLAG_EXTRA_AGGRO_ZONE));
		}

		if (curmana < creature_info_meta->MinLevelMana)
		{
			cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {} Entry: {}) with low current mana ({}), `creature_template`.`MinLevelMana`={}.", guid, id, curmana, creature_info_meta->MinLevelMana));
			curmana = creature_info_meta->MinLevelMana;
		}

		if (spawndist < 0.0f)
		{
			cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {} Entry: {}) with `spawndist`< 0, set to 0.", guid, id));
			spawndist = 0.0f;
		}
		else if (movementType == RANDOM_MOTION_TYPE)
		{
			if (spawndist == 0.0f)
			{
				cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {} Entry: {}) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).", guid, id));
				movementType = IDLE_MOTION_TYPE;
			}
		}
		else if (movementType == IDLE_MOTION_TYPE)
		{
			if (spawndist != 0.0f)
			{
				cftf::loger::SysLog::Instance().Error(std::format("Table `creature` have creature (GUID: {} Entry: {}) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, id));
				spawndist = 0.0f;
			}
		}

		if (gameEvent == 0 && GuidPoolId == 0 && EntryPoolId == 0) // if not this is to be managed by GameEvent System or Pool system
		{
			ObjectMgr::Instance().AddCreatureToGrid(guid, this);

			if (creature_info_meta->ExtraFlags & CREATURE_FLAG_EXTRA_ACTIVE)
			{
				cftf::loger::SysLog::Instance().Info(std::format("Adding `creature` with Active Flag: Map: {}, Guid {}", mapid, guid));
				active_creature_guids_onmap_.insert(std::make_pair(mapid, guid));
			}
		}
	}

}